/* 
 * File:   Element.cpp
 * Author: H4314
 * 
 * Created on 3 avril 2012, 09:46
 */
#include <list>
#include <sstream>
#include "XMLParse.h"

#include "Element.h"

Element::Element(const string & name, const string & nameSpace, const list<Node *> & children, const list<Attribute *> & attributes)
: name(name), nameSpace(nameSpace), children(children), attributes(attributes)
{
    NODES++;
    
#ifdef SHOW_FREE
        cout << "ADD: ELEMENT(" << name << ")"<< endl;
#endif
    
    childrenHasChanged();
}

Element::Element(const Element& orig) {
    list<Node *>::const_iterator itn;
    list<Attribute *>::const_iterator ita;
    NODES++;
    
#ifdef SHOW_FREE
        cout << "ADD: ELEMENT(" << name << ")"<< endl;
#endif
        
    this->name = orig.name;
    this->nameSpace = orig.nameSpace;
    
    // Copie des enfants
    for(itn = orig.children.begin(); itn != orig.children.end(); itn++)
    {
        Element * element = dynamic_cast<Element *>(*itn);
        Content * content = dynamic_cast<Content *>(*itn);
        
        if(element != NULL)
            this->children.push_back((Node *) new Element(*element));
        else if(content != NULL)
            this->children.push_back((Node *) new Content(*content));
    }

    // Copie des attributs
    for(ita = orig.attributes.begin(); ita != orig.attributes.end(); ita++)
        this->attributes.push_back(new Attribute(**ita));
}

Element::~Element() {
    NODES--;
    
#ifdef SHOW_FREE
        cout << "DEL: ELEMENT(" << name << ")"<< endl;
#endif
    
    Node::CleanItUp(children);
    Attribute::CleanItUp(attributes);
}

// MAJ de containsElement
void Element::childrenHasChanged() {
    list<Node *>::const_iterator it;
    containsElement = false;
    
    for(it = children.begin(); !containsElement && it != children.end(); it++)
    {
        Element * element = dynamic_cast<Element *>(*it);
 
        if(element != NULL)
            containsElement = true;
    }
}

// Getters
const string & Element::getName() const {
    return name;
}

const string & Element::getNameSpace() const {
    return nameSpace;
}

const list<Node *> & Element::getChildren() const {
    return children;
}

const list<Attribute *> & Element::getAttributes() const {
    return attributes;
}

// Setters
void Element::setName(const string & newName) {
    this->name = newName;
}

void Element::setNameSpace(const string & newNameSpace) {
    this->nameSpace = newNameSpace;
}

void Element::setChildren(const list<Node *> & newChildren) {
    //Node::CleanItUp(children);
    this->children = newChildren;
    childrenHasChanged();
}

void Element::setAttributes(const list<Attribute *> & newAttributes) {
    this->attributes = newAttributes;
}

// Adders
void Element::addChild(Node * child) {
    children.push_back(child);
    Element * element = dynamic_cast<Element *>(child);
    containsElement = containsElement || (element != NULL);
}

void Element::addAttribute(Attribute * attribute) {
    this->attributes.push_back(attribute);
}

// Affiche le contenu de l'objet
void Element::Print(ostream & stream) const {
    stream << nameSpace << (nameSpace.size() > 0 ? "::" : "") << name << " (Attrs: "<< attributes.size() << ", Child: " << children.size() << ")";
}

/**
* Affiche le sous-arbre DOM dont l'élément est la racine sous forme d'arbre
*/
void Element::Tree(ostream & stream, int level) const {
    list<Node *>::const_iterator nodeIt;
    list<Attribute *>::const_iterator attIt;
    string indent;
    
    // Gestion du niveau d'indentation
    for(int i = 0; i < level; i++)
        indent += "│\t";
    level++;
    
    stream << indent << "├── " << *this;
    
    // Parcours des Attributs
    int i(0), size(attributes.size());
    if(size > 0)
        stream << "[";
    for(attIt = attributes.begin(); attIt != attributes.end(); attIt++) {
        stream << **attIt;
        if(size > 1 && i < (size-1))
            stream << " ";
        i++;
    }
    if(size > 0)
        stream << "]";
    stream<< endl;
    
    // Parcours des enfants
    for(nodeIt = children.begin(); nodeIt != children.end(); nodeIt++) {
        (*nodeIt)->Tree(stream, level);
    }
}

/**
* Affiche le sous-arbre DOM dont l'élément est la racine sous forme d'xml
*/
void Element::Draw(ostream & stream, int level, bool colored) const {
    list<Node *>::const_iterator it;
    string indent;
    
    // Gestion du niveau d'indentation
    for(int i = 0; i < level; i++)
        indent += "  ";
    
    if(children.empty())
        stream << indent << this->toString(TAG, "", colored);
    else
    {
        level++;
        stream << indent << this->toString(TAG_BEGIN, "", colored);
        
        for(it = children.begin(); it != children.end(); it++)
        {
            Element * element = dynamic_cast<Element *>(*it);
            if(element != NULL)
                (*it)->Draw(stream, level, colored);
            else
                (*it)->Draw(stream, (containsElement ? level : 0), colored);
        }
        
        stream << this->toString(TAG_END, indent, colored);
    }
}

// Retourne 
bool Element::hasChildren() const {
    return !children.empty();
}

string Element::toString(int tagMode, const string & indent, bool colored) const {
    list<Attribute *>::const_iterator it;
    stringstream result, attrs("");
    string cName;
    
    // Coloration
    if(colored)
    {
        cName = "\033[34m" + name + "\033[00m";
        
        // Parcours des attributs
        for(it = attributes.begin(); it != attributes.end(); it++)
            attrs << " \033[35m" << (*it)->getName() << "\033[00m=\"\033[36m" << (*it)->getValue() << "\033[00m\"";
        
    }
    else
    {
        cName = name;
        
        // Parcours des attributs
        for(it = attributes.begin(); it != attributes.end(); it++)
            attrs << " " << (*it)->getName() << "=\"" << (*it)->getValue() << "\"";        
    }
    
    switch(tagMode)
    {
        case TAG_BEGIN:
            result << "<" << cName << attrs.str() << ">";
            if(containsElement)
                result << endl;
            break;
        case TAG:
            result << "<" << cName << attrs.str() << "/>" << endl;
            break;
        case TAG_END:
            if(containsElement)
                result << indent;
            result << "</" << cName << ">" << endl;
            break;
    }
    
    return result.str();
}

string Element::createValidationString() const {
    string validationString;
    list<Node*>::const_iterator it_node;
    for (it_node = children.begin(); it_node != children.end(); it_node++){
	/* 1st add separator */
	if (it_node != children.begin())
	    validationString += ",";
	/* 2nd write the name of the child if it's an Element or #PCDATA if it's a text content */
	Element * element = dynamic_cast<Element *>(*it_node);
	if(element != NULL)
	{
	    validationString += element->name;
	}
	else
	{
	    validationString += "#PCDATA";
	}
    }
    /* end the validationString */

    return validationString;
}

bool Element::validationWithDTD(map<string,regex> *regexs) const{

    bool val_return = false;
    string toBeValidate = createValidationString();
    cout << "Chaine XML" << "\t" << toBeValidate << endl;
    map<string,regex>::iterator regularExpression;

    if( regexs->count(name) > 0 )
    {
	regularExpression = regexs->find( name );
	val_return = regex_match( toBeValidate, regularExpression->second );
    }

    // First: validate the xml node
    if (!val_return) 
    {
	cout << "non valide: un noeud \"" << createValidationString() << "\"" << endl;
	cout <<  "ne valide pas d'expression régulière" << endl;
	return false;	// if the Node is not correct no need to continu
    }

    // start a recursive loop on the children to validate the full tree
    list<Node *>::iterator children;
    list<Node *> liste_de_pNode = getChildren();
    for (children = liste_de_pNode.begin() ; children != liste_de_pNode.end() ; children++) { 
	if (!(*children)->validationWithDTD(regexs))
	    return false; // a child doesn't validate the DTD
    }
    return true;
}
